<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="exbrD" id="exbrD"><span data-lake-id="uef8a7489" id="uef8a7489">典型回答</span></h1>
  <p data-lake-id="u505aa1b6" id="u505aa1b6"><br></p>
  <p data-lake-id="u585cfd2c" id="u585cfd2c"><span data-lake-id="udea5dddf" id="udea5dddf">想要让三个线程依次执行，并且严格按照T1,T2,T3的顺序的话，主要就是想办法让三个线程之间可以通信、或者可以排队。</span></p>
  <p data-lake-id="udebb8a3f" id="udebb8a3f"><span data-lake-id="u2d25c641" id="u2d25c641">​</span><br></p>
  <p data-lake-id="udb7573e5" id="udb7573e5"><span data-lake-id="u5d260787" id="u5d260787">想让多个线程之间可以通信，可以通过join方法实现，还可以通过CountDownLatch、CyclicBarrier和</span><span data-lake-id="u40ad0115" id="u40ad0115" class="lake-fontsize-12" style="color: rgb(32, 33, 36)">Semaphore来实现通信。</span></p>
  <p data-lake-id="u23f6c8a0" id="u23f6c8a0"><span data-lake-id="u0d936e06" id="u0d936e06" class="lake-fontsize-12" style="color: rgb(32, 33, 36)">​</span><br></p>
  <p data-lake-id="u755b7b6a" id="u755b7b6a"><span data-lake-id="u77238b52" id="u77238b52" class="lake-fontsize-12" style="color: rgb(32, 33, 36)">想要让线程之间排队的话，可以通过线程池或者CompletableFuture的方式来实现。</span></p>
  <h1 data-lake-id="CBBps" id="CBBps"><span data-lake-id="ud2c7fcb1" id="ud2c7fcb1">扩展知识</span></h1>
  <h2 data-lake-id="vf6mN" id="vf6mN"><span data-lake-id="ua4e9d34e" id="ua4e9d34e">依次执行start方法</span></h2>
  <p data-lake-id="u486a20de" id="u486a20de"><span data-lake-id="ua64cbf22" id="ua64cbf22">在代码中，分别依次调用三个线程的start方法，这种方法是最容易想到的，但是也是最不靠谱的。</span></p>
  <p data-lake-id="uf744df7e" id="uf744df7e"><span data-lake-id="ufa0e736c" id="ufa0e736c">​</span><br></p>
  <p data-lake-id="u9b9e2ad7" id="u9b9e2ad7"><span data-lake-id="ua5ef6add" id="ua5ef6add">代码实现如下，通过执行的话可以发现，数据结果是不固定的：</span></p>
  <p data-lake-id="u5f2d8634" id="u5f2d8634"><span data-lake-id="ub8552d65" id="ub8552d65">​</span><br></p>
  <pre lang="java"><code>
    public static void main(String[] args) {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread 1 running");
            }
        });


        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread 2 running");
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread 3 running");
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
    }
</code></pre>
  <p data-lake-id="uf9694a48" id="uf9694a48"><span data-lake-id="uf5bc277b" id="uf5bc277b">​</span><br></p>
  <p data-lake-id="ud75668e8" id="ud75668e8"><span data-lake-id="u70b587a4" id="u70b587a4">以上代码的数据结果每次执行都不固定，所以，没办法满足我们的要求。</span></p>
  <p data-lake-id="ud5b07329" id="ud5b07329"><span data-lake-id="ue0295f97" id="ue0295f97">​</span><br></p>
  <h2 data-lake-id="aVluP" id="aVluP"><span data-lake-id="ue5cfcc61" id="ue5cfcc61">使用join</span></h2>
  <p data-lake-id="ubd055825" id="ubd055825"><br></p>
  <p data-lake-id="uf0bacf40" id="uf0bacf40"><span data-lake-id="u38b9ed6a" id="u38b9ed6a">Thread类中提供了一个join方法，他的有以下代码：</span></p>
  <p data-lake-id="u16ed9c1d" id="u16ed9c1d"><span data-lake-id="u0231376b" id="u0231376b">​</span><br></p>
  <pre lang="java"><code>
public static void main(String[] args) throws InterruptedException {
    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread 1 running");
        }
    });

    thread1.start();

    System.out.println("Main 1 running");
}
</code></pre>
  <p data-lake-id="u67bdd70e" id="u67bdd70e"><span data-lake-id="u62e21394" id="u62e21394"></span></p>
  <p data-lake-id="u96f94172" id="u96f94172"><span data-lake-id="u67816733" id="u67816733">输出结果会是：</span></p>
  <p data-lake-id="ub17ef7db" id="ub17ef7db"><span data-lake-id="ufbc2e4cf" id="ufbc2e4cf">​</span><br></p>
  <pre lang="java"><code>
Main 1 running
Thread 1 running
</code></pre>
  <p data-lake-id="u2a2326b5" id="u2a2326b5"><span data-lake-id="u51ea38cc" id="u51ea38cc">​</span><br></p>
  <p data-lake-id="ue0f47e61" id="ue0f47e61"><span data-lake-id="u3fc9c0d0" id="u3fc9c0d0">但是，如果我们在上面的第15行，增加一行</span><code data-lake-id="u2a2b3c80" id="u2a2b3c80"><span data-lake-id="u2e5d3743" id="u2e5d3743">thread1.join();&#x0;</span></code><span data-lake-id="u6d508ec1" id="u6d508ec1">那么输出结果就会有变化，如下：</span></p>
  <p data-lake-id="ud3cb5014" id="ud3cb5014"><span data-lake-id="u5afe9975" id="u5afe9975">​</span><br></p>
  <pre lang="java"><code>
Thread 1 running
Main 1 running
</code></pre>
  <p data-lake-id="u54f0be92" id="u54f0be92"><span data-lake-id="u972a6443" id="u972a6443">​</span><br></p>
  <p data-lake-id="u72081638" id="u72081638"><span data-lake-id="u26979728" id="u26979728">所以，join就是把thread1这个子线程加入到当前主线程中，也就是主线程要阻塞在这里，等子线程执行完之后再继续执行。</span></p>
  <p data-lake-id="uc6976c9e" id="uc6976c9e"><span data-lake-id="uef7e75a4" id="uef7e75a4">​</span><br></p>
  <p data-lake-id="u4b579c99" id="u4b579c99"><span data-lake-id="u36e8752a" id="u36e8752a">所以，我们可以通过join来实现多个线程的顺序执行：</span></p>
  <p data-lake-id="u01b8cf02" id="u01b8cf02"><span data-lake-id="udfa04c06" id="udfa04c06">​</span><br></p>
  <pre lang="java"><code>
    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " is Running.");
            }
        },"T1");


        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread1.join();
                } catch (InterruptedException e) {
                    System.out.println("join thread1 failed");
                }
                System.out.println(Thread.currentThread().getName() + " is Running.");
            }
        },"T2");

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.join();
                } catch (InterruptedException e) {
                    System.out.println("join thread1 failed");
                }
                System.out.println(Thread.currentThread().getName() + " is Running.");
            }
        },"T3");

        thread3.start();
        thread2.start();
        thread1.start();
    }
</code></pre>
  <p data-lake-id="ue11fe442" id="ue11fe442"><span data-lake-id="u0a2b9af7" id="u0a2b9af7">​</span><br></p>
  <p data-lake-id="u85ee25bd" id="u85ee25bd"><span data-lake-id="u41f73199" id="u41f73199">我们在thread2中等待thread1执行完，然后在thread3中等待thread2执行完。那么整体的执行顺序就是：</span></p>
  <p data-lake-id="u98f85d16" id="u98f85d16"><span data-lake-id="u4ed2dc7b" id="u4ed2dc7b">​</span><br></p>
  <pre lang="java"><code>
T1 is Running.
T2 is Running.
T3 is Running.
</code></pre>
  <p data-lake-id="u0576a840" id="u0576a840"><br></p>
  <h2 data-lake-id="zITf4" id="zITf4"><span data-lake-id="u7ff5736c" id="u7ff5736c">使用CountDownLatch</span></h2>
  <p data-lake-id="ubb5d6068" id="ubb5d6068"><br></p>
  <p data-lake-id="u648fd7e1" id="u648fd7e1"><span data-lake-id="ufed99158" id="ufed99158">CountDownLatch是Java并发库中的一个同步辅助类，它允许一个或多个线程等待其他线程完成操作。我们可以借助他来让三个线程之间相互通信，以达到顺序执行的目的。</span></p>
  <p data-lake-id="uc5c47996" id="uc5c47996"><span data-lake-id="u6ce954ab" id="u6ce954ab">​</span><br></p>
  <pre lang="java"><code>
public class CountDownLatchThreadExecute {
    public static void main(String[] args) throws InterruptedException {
                // 创建CountDownLatch对象，用来做线程通信
        CountDownLatch latch = new CountDownLatch(1);
        CountDownLatch latch2 = new CountDownLatch(1);
        CountDownLatch latch3 = new CountDownLatch(1);

        // 创建并启动线程T1
        Thread t1 = new Thread(new MyThread(latch), "T1");
        t1.start();

        // 等待线程T1执行完
        latch.await();

        // 创建并启动线程T2
        Thread t2 = new Thread(new MyThread(latch2), "T2");
        t2.start();

        // 等待线程T2执行完
        latch2.await();

        // 创建并启动线程T3
        Thread t3 = new Thread(new MyThread(latch3), "T3");
        t3.start();

        // 等待线程T3执行完
        latch3.await();
    }
}

class MyThread implements Runnable {
    private CountDownLatch latch;

    public MyThread(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 完成一个线程，计数器减1
            latch.countDown();
        }
    }
}
</code></pre>
  <p data-lake-id="ue4803d82" id="ue4803d82"><br></p>
  <p data-lake-id="u40294cca" id="u40294cca"><span data-lake-id="u31857c4f" id="u31857c4f">主要就是想办法让编排三个子线程的主线程阻塞，保证T1执行完再启动T2，T2执行完再启动T3。而这个编排的方式就是想办法知道什么时候子线程执行完，就可以通过CountDownLatch实现。</span></p>
  <p data-lake-id="ud6a6b74a" id="ud6a6b74a"><span data-lake-id="u439fc438" id="u439fc438">​</span><br></p>
  <p data-lake-id="u2fc8a67c" id="u2fc8a67c"><span data-lake-id="u89f3eb02" id="u89f3eb02">基于相同的原理， 我们还可以借助CyclicBarrier和</span><span data-lake-id="ue2e64224" id="ue2e64224" class="lake-fontsize-12" style="color: rgb(32, 33, 36)">Semaphore</span><span data-lake-id="ubf5cb329" id="ubf5cb329">实现此功能：</span></p>
  <p data-lake-id="u3bf6bb3a" id="u3bf6bb3a"><span data-lake-id="ufede2cf8" id="ufede2cf8">​</span><br></p>
  <pre lang="java"><code>

public class CyclicBarrierThreadExecute {

    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        // 创建CyclicBarrier对象，用来做线程通信
        CyclicBarrier barrier = new CyclicBarrier(2);

        // 创建并启动线程T1
        Thread t1 = new Thread(new MyThread(barrier), "T1");
        t1.start();

        // 等待线程T1执行完
        barrier.await();

        // 创建并启动线程T2
        Thread t2 = new Thread(new MyThread(barrier), "T2");
        t2.start();

        // 等待线程T2执行完
        barrier.await();

        // 创建并启动线程T3
        Thread t3 = new Thread(new MyThread(barrier), "T3");
        t3.start();

        // 等待线程T3执行完
        barrier.await();
    }
}

class MyThread implements Runnable {
    private CyclicBarrier barrier;

    public MyThread(CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 等待其他线程完成
            try {
                barrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}


</code></pre>
  <p data-lake-id="u09c8c975" id="u09c8c975"><span data-lake-id="udbd8bca8" id="udbd8bca8">​</span><br></p>
  <p data-lake-id="uf95195f6" id="uf95195f6"><span data-lake-id="uc567e408" id="uc567e408" class="lake-fontsize-12" style="color: rgb(32, 33, 36)">借助Semaphore</span><span data-lake-id="u4657c75a" id="u4657c75a">实现此功能：</span></p>
  <pre lang="java"><code>

public class SemaphoreThreadExecute {
    public static void main(String[] args) throws InterruptedException {
        // 创建Semaphore对象，用来做线程通信
        Semaphore semaphore = new Semaphore(1);

        // 等待线程T1执行完
        semaphore.acquire();

        // 创建并启动线程T1
        Thread t1 = new Thread(new MyThread(semaphore), "T1");
        t1.start();

        // 等待线程T2执行完
        semaphore.acquire();

        // 创建并启动线程T2
        Thread t2 = new Thread(new MyThread(semaphore), "T2");
        t2.start();

        // 等待线程T3执行完
        semaphore.acquire();

        // 创建并启动线程T3
        Thread t3 = new Thread(new MyThread(semaphore), "T3");
        t3.start();
    }
}

class MyThread implements Runnable {
    private Semaphore semaphore;

    public MyThread(Semaphore semaphore) {
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放许可证，表示完成一个线程
            semaphore.release();
        }
    }
}
</code></pre>
  <p data-lake-id="u46ac6ff9" id="u46ac6ff9"><br></p>
  <h2 data-lake-id="p4luc" id="p4luc"><span data-lake-id="u9a10dfdf" id="u9a10dfdf">使用线程池</span></h2>
  <p data-lake-id="u50bcaee1" id="u50bcaee1"><br></p>
  <p data-lake-id="u3ff4f632" id="u3ff4f632"><span data-lake-id="u31e349f5" id="u31e349f5">了解线程池的开发者都知道，线程池内部是使用了队列来存储任务的，所以线程的执行顺序会按照任务的提交顺序执行的，但是如果是多个线程同时执行的话，是保证不了先后顺序的，因为可能先提交的后执行了。但是我们可以定义一个只有一个线程的线程池，然后依次的将T1,T2,T3提交给他执行：</span></p>
  <p data-lake-id="udb164919" id="udb164919"><span data-lake-id="u137329e4" id="u137329e4">​</span><br></p>
  <pre lang="java"><code>
public class ThreadPoolThreadExecute {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 创建并启动线程T1
        executor.submit(new MyThread("T1"));

        // 创建并启动线程T2
        executor.submit(new MyThread("T2"));

        // 创建并启动线程T3
        executor.submit(new MyThread("T3"));

        // 关闭线程池
        executor.shutdown();
    }
}

class MyThread implements Runnable {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(name + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
  <p data-lake-id="uf25afd12" id="uf25afd12"><br></p>
  <h2 data-lake-id="C5FBM" id="C5FBM"><span data-lake-id="u16187d71" id="u16187d71">使用CompletableFuture</span></h2>
  <p data-lake-id="u51e565d3" id="u51e565d3"><br></p>
  <p data-lake-id="u8f89e725" id="u8f89e725"><span data-lake-id="ue7b91bc0" id="ue7b91bc0">Java 8引入了CompletableFuture，它是一个用于异步编程的新的强大工具。CompletableFuture提供了一系列的方法，可以用来创建、组合、转换和管理异步任务，并且可以让你实现异步流水线，在多个任务之间轻松传递结果。</span></p>
  <p data-lake-id="ube40da17" id="ube40da17"><span data-lake-id="uf2963c74" id="uf2963c74">​</span><br></p>
  <p data-lake-id="u7522ee4c" id="u7522ee4c"><span data-lake-id="u4422f09b" id="u4422f09b">如以下实现方式：</span></p>
  <p data-lake-id="u7f6dc828" id="u7f6dc828"><span data-lake-id="u7cf4de01" id="u7cf4de01">​</span><br></p>
  <pre lang="java"><code>
public class CompletableFutureThreadExecute {
    public static void main(String[] args) {
        // 创建CompletableFuture对象
        CompletableFuture&lt;Void&gt; future1 = CompletableFuture.runAsync(new MyThread("T1"));

        // 等待线程T1完成
        future1.join();

        // 创建CompletableFuture对象
        CompletableFuture&lt;Void&gt; future2 = CompletableFuture.runAsync(new MyThread("T2"));

        // 等待线程T2完成
        future2.join();

        // 创建CompletableFuture对象
        CompletableFuture&lt;Void&gt; future3 = CompletableFuture.runAsync(new MyThread("T3"));

        // 等待线程T3完成
        future3.join();
    }
}

class MyThread implements Runnable {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(name + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
  <p data-lake-id="ud83fe86c" id="ud83fe86c"><span data-lake-id="u71aaab05" id="u71aaab05">​</span><br></p>
  <p data-lake-id="u7da5670e" id="u7da5670e"><span data-lake-id="u75c30046" id="u75c30046">上面的代码还可以做一些优化：</span></p>
  <p data-lake-id="u9050109d" id="u9050109d"><span data-lake-id="ucbdf7c6f" id="ucbdf7c6f">​</span><br></p>
  <pre lang="java"><code>
public class CompletableFutureThreadExecute {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建CompletableFuture对象
        CompletableFuture&lt;Void&gt; future = CompletableFuture.runAsync(new MyThread("T1")).thenRun(new MyThread("T2")).thenRun(new MyThread("T3"));
        future.get();
    }
}

class MyThread implements Runnable {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(name + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
 </body>
</html>